#ifdef DEBUG_LISTFIFO
#include <stdio.h>
#endif

#include <stdlib.h>
#include "listfifostack.h"

#define LIST_FIFO_STACK_NULL_RETURN(check, ret)   \
    if( NULL == (check)){                   \
        return (ret);                       \
    }

#define LIST_FIFO_STACK_SET_ERR(fs, err)         \
        if( LIST_FIFO_STACK_SUCCESS == (err) ) {   \
            (fs)->has_error = FALSE;  \
        } else {                        \
            (fs)->has_error = TRUE;   \
        }                               \
        (fs)->error_code = err;

#define LIST_FIFO_STACK_COPY_ERR(fs)                        \
        (fs)->has_error = (fs)->__list->has_error;    \
        (fs)->error_code = (fs)->__list->error_code;

static list_fs_t *__fs_new(data_op_t * default_callback)
{
	list_fs_t *fs =
	    (list_fs_t *) malloc(sizeof(list_fs_t));
	LIST_FIFO_STACK_NULL_RETURN(fs, NULL);
	LIST_FIFO_STACK_SET_ERR(fs, LIST_FIFO_STACK_SUCCESS);

	fs->__list = list_new(default_callback);
	if (NULL == fs->__list) {
		free(fs);
		return NULL;
	}
	return fs;
}

list_fifo_t *list_fifo_new(data_op_t * default_callback)
{
	return (list_fifo_t *) __fs_new(default_callback);
}

list_stack_t *list_stack_new(data_op_t * default_callback)
{
	return (list_stack_t *) __fs_new(default_callback);
}

static list_fs_t *__fs_new_with_copy(list_fs_t *
							  from,
							  func_copy_t func_copy)
{
	list_fs_t *fs =
	    (list_fs_t *) malloc(sizeof(list_fs_t));
	LIST_FIFO_STACK_NULL_RETURN(fs, NULL);
	LIST_FIFO_STACK_SET_ERR(fs, LIST_FIFO_STACK_SUCCESS);

	fs->__list = list_new_with_copy(from->__list, func_copy);
	if (NULL == fs->__list) {
		free(fs);
		return NULL;
	}
	return fs;
}

list_fifo_t *list_fifo_new_with_copy(list_fifo_t * from, func_copy_t func_copy)
{
	return (list_fifo_t *)
	    __fs_new_with_copy((list_fs_t *) from,
					    func_copy);
}

list_stack_t *list_stack_new_with_copy(list_stack_t * from,
				       func_copy_t func_copy)
{
	return (list_stack_t *)
	    __fs_new_with_copy((list_fs_t *) from,
					    func_copy);
}

static const char *__get_last_error_string(list_fs_t * fs)
{
	if (NULL == fs) {
		return NULL;
	}
	return list_get_last_error_string(fs->__list);
}

const char *list_fifo_get_last_error_string(list_fifo_t * fifo)
{
	return __get_last_error_string((list_fs_t *) fifo);
}

const char *list_stack_get_last_error_string(list_stack_t * stack)
{
	return __get_last_error_string((list_fs_t *) stack);
}

static const char *__get_err_string(const int error_code)
{
	return list_get_error_string(error_code);
}

const char *list_fifo_get_error_string(const int error_code)
{
	return __get_err_string(error_code);
}

const char *list_stack_get_error_string(const int error_code)
{
	return __get_err_string(error_code);
}

static int __reset_callbacks(list_fs_t * fs,
			     data_op_t * default_callback)
{
	LIST_FIFO_STACK_NULL_RETURN(fs, LIST_FIFO_STACK_ERR_ARGVALUE);
	LIST_FIFO_STACK_SET_ERR(fs, LIST_FIFO_STACK_SUCCESS);

	int result = list_reset_callbacks(fs->__list, default_callback);
	LIST_FIFO_STACK_COPY_ERR(fs);

	return result;
}

int list_fifo_reset_callbacks(list_fifo_t * fifo, data_op_t * default_callback)
{
	return __reset_callbacks((list_fs_t *) fifo, default_callback);
}

int list_stack_reset_callbacks(list_stack_t * stack,
			       data_op_t * default_callback)
{
	return __reset_callbacks((list_fs_t *) stack, default_callback);
}

static inline int __fs_size(list_fs_t * fs)
{
	LIST_FIFO_STACK_NULL_RETURN(fs, LIST_FIFO_STACK_ERR_ARGVALUE);

	int len = list_size(fs->__list);
	LIST_FIFO_STACK_COPY_ERR(fs);

	return len;
}

inline int list_fifo_size(list_fifo_t * fifo)
{
	return __fs_size((list_fs_t *) fifo);
}

inline int list_stack_size(list_stack_t * stack)
{
	return __fs_size((list_fs_t *) stack);
}

static int __fs_contains(list_fs_t * fs, const void *data,
				 func_cmp_t func_cmp)
{
	LIST_FIFO_STACK_NULL_RETURN(fs, LIST_FIFO_STACK_ERR_ARGVALUE);
	int result = 0;

	result = list_contains(fs->__list, data, func_cmp);
	LIST_FIFO_STACK_COPY_ERR(fs);

	return result;
}

int list_fifo_contains(list_fifo_t * fifo, const void *data,
		       func_cmp_t func_cmp)
{
	return __fs_contains((list_fs_t *) fifo, data,
				     func_cmp);
}

int list_stack_contains(list_stack_t * stack, const void *data,
			func_cmp_t func_cmp)
{
	return __fs_contains((list_fs_t *) stack, data,
				     func_cmp);
}

int list_fifo_push(list_fifo_t * fifo, const void *data,
		   const func_copy_t func_copy)
{
	LIST_FIFO_STACK_NULL_RETURN(fifo, LIST_FIFO_STACK_ERR_ARGVALUE);

	int result = -1;

	result = list_add_last(fifo->__list, data, func_copy);
	LIST_FIFO_STACK_COPY_ERR(fifo);

	return result;
}

int list_stack_push(list_stack_t * stack, const void *data,
		    const func_copy_t func_copy)
{
	LIST_FIFO_STACK_NULL_RETURN(stack, LIST_FIFO_STACK_ERR_ARGVALUE);

	int result = -1;

	result = list_add_first(stack->__list, data, func_copy);
	LIST_FIFO_STACK_COPY_ERR(stack);

	return result;
}

static void *__fs_front(list_fs_t * fs);
static void *__fs_pop(list_fs_t * fs, void **data,
			      const func_copy_t func_copy,
			      const func_free_t func_free)
{
	LIST_FIFO_STACK_NULL_RETURN(fs, NULL);

	void *copyResult = NULL;
	func_copy_t do_copy =
	    (func_copy ==
	     NULL) ? fs->__list->__default_callback->copy : func_copy;

	if (NULL != do_copy) {
		copyResult = do_copy(data, __fs_front(fs));
	} else {
		copyResult = *data = __fs_front(fs);
	}
	if (copyResult == NULL) {
		LIST_FIFO_STACK_SET_ERR(fs, LIST_FIFO_STACK_ERR_CALLBACK);
		return NULL;
	}

	list_del_first(fs->__list, func_free);
	LIST_FIFO_STACK_COPY_ERR(fs);

	return copyResult;
}

void *list_fifo_pop(list_fifo_t * fifo, void **data,
		    const func_copy_t func_copy, const func_free_t func_free)
{
	return __fs_pop((list_fs_t *) fifo, data, func_copy,
				func_free);
}

void *list_stack_pop(list_stack_t * stack, void **data,
		     const func_copy_t func_copy, const func_free_t func_free)
{
	return __fs_pop((list_fs_t *) stack, data, func_copy,
				func_free);
}

static void *__fs_front(list_fs_t * fs)
{
	LIST_FIFO_STACK_NULL_RETURN(fs, NULL);
	void *result = NULL;

	result = list_get_first(fs->__list);
	LIST_FIFO_STACK_COPY_ERR(fs);

	return result;
}

void *list_fifo_front(list_fifo_t * fifo)
{
	return __fs_front((list_fs_t *) fifo);
}

void *list_stack_top(list_stack_t * stack)
{
	return __fs_front((list_fs_t *) stack);
}

static void *__fs_destroy(list_fs_t ** fs,
				  const func_free_t func_free)
{
	LIST_FIFO_STACK_NULL_RETURN(*fs, NULL);

	void *result = list_destroy(&((*fs)->__list), func_free);
	LIST_FIFO_STACK_SET_ERR(*fs,
				((result ==
				  NULL) ? LIST_FIFO_STACK_SUCCESS :
				 LIST_FIFO_STACK_ERR_UNKNOWN));

	free(*fs);
	*fs = NULL;
	return NULL;
}

void *list_fifo_destroy(list_fifo_t ** fifo, const func_free_t func_free)
{
	return __fs_destroy((list_fs_t **) fifo, func_free);
}

void *list_stack_destroy(list_stack_t ** stack, const func_free_t func_free)
{
	return __fs_destroy((list_fs_t **) stack, func_free);
}

int __fs_clear(list_fs_t * fs, const func_free_t func_free)
{
	LIST_FIFO_STACK_NULL_RETURN(fs, LIST_FIFO_STACK_ERR_ARGVALUE);

	int ret = list_clear(fs->__list, func_free);

	LIST_FIFO_STACK_COPY_ERR(fs);
	return ret;
}

int list_fifo_clear(list_fifo_t * fifo, const func_free_t func_free)
{
	return __fs_clear((list_fs_t *) fifo, func_free);
}

int list_stack_clear(list_stack_t * stack, const func_free_t func_free)
{
	return __fs_clear((list_fs_t *) stack, func_free);
}

list_fs_t *__fs_join(list_fs_t * dst,
				     list_fs_t * src,
				     func_copy_t func_copy,
				     func_free_t func_free)
{
	LIST_FIFO_STACK_NULL_RETURN(dst, NULL);
	LIST_FIFO_STACK_NULL_RETURN(src, NULL);

	list_join(dst->__list, src->__list, func_copy, func_free);

	LIST_FIFO_STACK_COPY_ERR(dst);
	return dst;
}

list_stack_t *list_stack_join(list_stack_t * dst, const list_stack_t * src,
			      func_copy_t func_copy, func_free_t func_free)
{
	return __fs_join((list_fs_t *) dst,
				 (list_fs_t *) src, func_copy,
				 func_free);
}

list_fifo_t *list_fifo_join(list_fifo_t * dst, const list_fifo_t * src,
			    func_copy_t func_copy, func_free_t func_free)
{
	return __fs_join((list_fs_t *) dst,
				 (list_fs_t *) src, func_copy,
				 func_free);
}
